package Lexical;

import java.io.*;
import java.util.*;

public class Lex {

    private ArrayList<String> keyWord = new ArrayList<String>();
    private ArrayList<String> delimiters = new ArrayList<String>();
    private ArrayList<String> operate = new ArrayList<String>();
    private ArrayList<String> identifier = new ArrayList<String>();
    private ArrayList<String> constants = new ArrayList<String>();
    private int keyWord_num;
    private int delimit_num;
    private int operate_num;
    private int ident_num;
    private int constants_num;

    private ArrayList<Transform> id_DFA = new ArrayList<Transform>();
    private ArrayList<Transform> const_DFA = new ArrayList<Transform>();

    public Lex( String fileAdd ){
        FileReader fr; //将 词法规则 读入
        BufferedReader bfr;
        try {
            fr = new FileReader(fileAdd);
            bfr = new BufferedReader(fr);

            int i = 0;
            keyWord_num = Integer.parseInt(bfr.readLine());
            for(i=0; i<keyWord_num; i++)
            {
                keyWord.add(bfr.readLine());
            }

            delimit_num = Integer.parseInt(bfr.readLine());
            for(i=0; i<delimit_num; i++)
            {
                delimiters.add(bfr.readLine());
            }

            operate_num = Integer.parseInt(bfr.readLine());
            for(i=0; i<operate_num; i++)
            {
                operate.add(bfr.readLine());
            }

            ident_num = Integer.parseInt(bfr.readLine());
            for(i=0; i<ident_num; i++)
            {
                identifier.add(bfr.readLine());
            }

            constants_num = Integer.parseInt(bfr.readLine());
            for(i=0; i<constants_num; i++)
            {
                constants.add(bfr.readLine());
            }
        }
        catch (FileNotFoundException e)
        {
            System.out.println("读取文件失败: "+fileAdd);
            System.exit(0);
        }
        catch (IOException ioe)
        {
            System.out.println("读取文件失败: "+fileAdd);
            System.exit(0);
        }
    }
    public Lex(){}

    public void show(){
        for(String obj: keyWord){
            System.out.println(obj);
        }

        for(String obj: delimiters){
            System.out.println(obj);
        }

        for(String obj: operate){
            System.out.println(obj);
        }

        for(String obj: identifier){
            System.out.println(obj);
        }

        for(String obj: constants){
            System.out.println(obj);
        }
    }

    public void showMTs(ArrayList<MultiTransform> mts)
    {
        for(MultiTransform o: mts)
        {
            System.out.println("current status:"+o.getCurrent_status());
            System.out.println("input:"+o.getInput());
            System.out.print("target status:");
            for(String t: o.getTarget_status())
            {
                System.out.print(" "+t);
            }
            System.out.println("\n");
        }
    }

    public void rule_to_MultiTransform(ArrayList<String> rules, ArrayList<MultiTransform> MTs, Set<String> terml_status,Set<String> rule_input)
    {
        if( rules.size()==0 )
        {
            System.out.println("输入规则过少");
            System.exit(0);
        }

        for(String rule: rules)// 将NFA对应的 多值函数 提取出来
        {
            if( rule.substring( rule.length()-1 ).equals("#") )
            {
                if( !terml_status.add( rule.substring(0,1) ))
                {
                    System.out.println("元素"+rule.substring(0,1)+"重复");
                }
                continue;
            }

            Transform temp = new Transform();
            temp.setCurrent_status( rule.substring(0,1) );
            temp.setInput( rule.substring(3,rule.length()-1 ) );
            rule_input.add(temp.getInput());
            temp.setTarget_status( rule.substring(  rule.length()-1 ) );

            int flag = 1;
            for(MultiTransform mt: MTs)
            {
                if( mt.isEqual( temp.getCurrent_status(), temp.getInput() ) )
                {
                    mt.addTargetStatus( temp.getTarget_status() );
                    flag = 0;
                    break;
                }
            }
            if( flag == 1 )
            {
                MultiTransform temp2 = new MultiTransform();
                temp2.setCurrent_status( temp.getCurrent_status() );
                temp2.setInput( temp.getInput() );
                temp2.addTargetStatus( temp.getTarget_status() );

                MTs.add(temp2);
            }

        }
    }

    public void NFA_to_DFA(){

        //先处理 标识符的 NFA 到 DFA
        //1.确定 初始状态 和 终止 状态
        //2.把 NFA 的转化函数 提取出来

        //multiTransform 指多值函数 transform指单值函数
        ArrayList<MultiTransform> id_MTs = new ArrayList<MultiTransform>();
        ArrayList<MultiTransform> const_MTs = new ArrayList<MultiTransform>();

        String id_init_status = identifier.get(0).substring(0,1);//记录 初态 是什么
        String const_init_status = constants.get(0).substring(0,1);

        Set<String> id_terml_status = new HashSet<String>();//记录 终态有哪些
        Set<String> const_terml_status = new HashSet<String>();
        Set<String> id_inputs = new HashSet<String>();//记录 标识符有哪些输入
        Set<String> const_inputs = new HashSet<String>();//记录 常量 可以有哪些输入

        this.rule_to_MultiTransform(identifier, id_MTs, id_terml_status, id_inputs);//将输入的 三型文法 转化为 多值函数
        this.rule_to_MultiTransform(constants, const_MTs, const_terml_status, const_inputs);


        /*System.out.println("初始状态为"+id_init_status);
        this.showMTs(id_MTs);//输出中间结果
        System.out.println("终态为：");
        for(String terml: id_terml_status)
        {
            System.out.println(terml);
        }

        System.out.println("初始状态为"+const_init_status);
        this.showMTs(const_MTs);
        System.out.println("终态为：");
        for(String terml: const_terml_status)
        {
            System.out.println(terml);
        }*/
        /*System.out.println("标识符的输入可以为：");
        for(String s: id_inputs)
        {
            System.out.println(s);
        }
        System.out.println("常量的输入可以为：");
        for(String s: const_inputs)
        {
            System.out.println(s);
        }*/

        //NFA到DFA  根据 多值函数 转化 到 单值函数
        Set<String> id_init_s_cloure = this.null_closure(id_init_status, id_MTs);
        Set<Set<String>> id_all_status = new HashSet<Set<String>>();//储存 closure(move()) 后产生的所有状态子集
        Set<Set<String>> id_once_status = new HashSet<Set<String>>();//存储单次move后 可能产生的 状态
        id_once_status.add(id_init_s_cloure);
        ArrayList<SetTransform> id_uniforms = new ArrayList<SetTransform>();// 暂存 由 状态集合 组成的 单值函数
        this.move(id_once_status, id_MTs, id_inputs, id_all_status, id_uniforms);
        /*System.out.println("标识符的 单值函数 列表：");
        for(SetTransform st: id_uniforms )
        {
            System.out.println("\n\n当前状态集合为");
            for(String s: st.getCurrent_status() )
            {
                System.out.print(" "+s);
            }
            System.out.println("\n输入为："+st.getInput());
            System.out.println("目标状态空闭包为：");
            for(String s: st.getTarget_status() )
            {
                System.out.print(" "+s);
            }
        }*/

        Set<String> const_init_s_cloure = this.null_closure(const_init_status, id_MTs);
        Set<Set<String>> const_all_status = new HashSet<Set<String>>();
        Set<Set<String>> const_once_status = new HashSet<Set<String>>();//存储单次move后 可能产生的 状态
        const_once_status.add(const_init_s_cloure);
        ArrayList<SetTransform> const_uniforms = new ArrayList<SetTransform>();// 暂存 由 状态集合 组成的 单值函数
        this.move(const_once_status, const_MTs, const_inputs, const_all_status, const_uniforms);
        /*System.out.println("\n常量的 单值函数 列表：");
        for(SetTransform st: const_uniforms )
        {
            System.out.println("\n\n当前状态集合为");
            for(String s: st.getCurrent_status() )
            {
                System.out.print(" "+s);
            }
            System.out.println("\n输入为："+st.getInput());
            System.out.println("目标状态空闭包为：");
            for(String s: st.getTarget_status() )
            {
                System.out.print(" "+s);
            }
        }*/

        //将 形如 f({A,B,C},input) = {E,F,G} 的单值函数变成 形如 f(1,input)=2 的单值函数
        //1.初始状态闭包 作为 初始状态
        //2.含终态闭包 为 终态
        //3.中间状态 统一 编号
        //先对 标识符的做处理
        this.SetTrans_to_DFA(id_all_status, id_uniforms, id_DFA, id_init_s_cloure, id_terml_status);
        this.SetTrans_to_DFA(const_all_status, const_uniforms, const_DFA, const_init_s_cloure, const_terml_status);
        /*System.out.println("展示标识符DFA");
        this.showDFA(id_DFA);
        System.out.println("展示常量DFA");
        this.showDFA(const_DFA);*/

    }

    public void move(Set<Set<String>> once_status, ArrayList<MultiTransform> MTs, Set<String> inputs,  Set<Set<String>> all_status, ArrayList<SetTransform> uniform)//NFA转DFA核心
    {
        if( once_status.isEmpty() )
            return;

        for(Set<String> status: once_status)
        {
            if( all_status.contains(status) )// NFA 到 DFA的转化 终止情况就是 没有新的 状态集合 加入
                continue;//已经计算过的 单值函数 就不要计算了  单值函数的键值key 是(当前状态，输入)
            else
                all_status.add(status);

            Set<Set<String>> temp_once_status = new HashSet<Set<String>>();
            for(String input: inputs)
            {
                Set<String> outs = new HashSet<String>();

                SetTransform st = new SetTransform();
                st.setCurrent_status(status);
                st.setInput(input);

                for(String single: status)
                {
                    //System.out.println("\n当前正再计算状态 "+single+" 在输入为 "+input+" 下的情况");//
                    for(MultiTransform mt: MTs)
                    {
                        if( mt.isEqual(single, input))
                        {
                            outs.addAll( mt.getTarget_status() );
                        }
                    }
                }
                Set<String> closure = this.null_closure(outs, MTs);
                /*System.out.println("对应的输出为：");//
                for(String s: closure)//
                {
                    System.out.print(" "+s);
                }*/
                if( !closure.isEmpty() )
                {
                    temp_once_status.add( closure  );
                    st.setTarget_status( closure );
                    uniform.add(st);
                }

            }
            /*System.out.println("\n该次遍历后产生的状态有：");//
            for(Set<String> sta: temp_once_status)//
            {
                for(String s: sta)
                {
                    System.out.print(" "+s);
                }
                System.out.println("\n");
            }*/
            this.move(temp_once_status, MTs, inputs, all_status, uniform);

        }

    }

    public void SetTrans_to_DFA(Set<Set<String>> all_status, ArrayList<SetTransform> STs, ArrayList<Transform> DFA, Set<String> init_closure, Set<String> terml_status )
    {
        Map<Set<String>,String> status_index = new HashMap<Set<String>, String>();
        int count_normal = 1;
        int count_terml = 1;
        for(Set<String> status: all_status)//给每个 状态集合 上编号
        {

            if( status.equals(init_closure) )
            {
                status_index.put(status, "init");
            }
            else if( this.hasTerml(status, terml_status) )
            {
                status_index.put(status,"terml_"+String.valueOf(count_terml));
                count_terml++;
            }
            else
            {
                status_index.put(status,String.valueOf(count_normal));
                count_normal++;
            }
        }
        for(SetTransform st: STs)
        {
            Transform temp = new Transform();
            temp.setCurrent_status( status_index.get(st.getCurrent_status()) );
            temp.setInput( st.getInput() );
            temp.setTarget_status( status_index.get(st.getTarget_status()) );
            DFA.add(temp);
        }
    }

    public void showDFA(ArrayList<Transform> DFA)
    {
        for(Transform t: DFA)
        {
            System.out.println("当前状态为："+t.getCurrent_status());
            System.out.println("输入为："+t.getInput());
            System.out.println("目标状态为"+t.getTarget_status()+"\n");
        }
    }
    public boolean hasTerml(Set<String> detection, Set<String> terml_status)//检测一个 状态集合中是否含有 终态
    {
        for(String terml: terml_status)
        {
            if(detection.contains(terml))
                return true;
        }
        return false;
    }

    public Set<String> null_closure(Set<String> current_status, ArrayList<MultiTransform> MTs)
    {
        Set<String> closure = new HashSet<String>();
        closure.addAll(current_status);
        for(String s: current_status)
        {
            for(MultiTransform mt: MTs)
            {
                if( mt.isEqual(s,"#") )
                {
                    closure.addAll(mt.getTarget_status());
                }
            }
        }
        return closure;
    }
    public Set<String> null_closure(String current_status, ArrayList<MultiTransform> MTs)
    {
        Set<String> closure = new HashSet<String>();
        closure.add(current_status);
        for(MultiTransform mt: MTs)
        {
            if( mt.isEqual(current_status,"#") )
            {
                closure.addAll(mt.getTarget_status());
            }
        }
        return closure;
    }


    //扫描 源码 进行词法分析 识别token
    public ArrayList<Token> scanCode(String codeSourceFile, String storeResult)
    {
        //读取一行 进行识别
        FileReader input_fr;
        BufferedReader input_bfr;
        FileWriter write_fr;
        BufferedWriter write_bfr;
        ArrayList<Token> result = new ArrayList<Token>();
        try {
            input_fr = new FileReader(codeSourceFile);
            input_bfr = new BufferedReader(input_fr);

            write_fr = new FileWriter(storeResult);
            write_bfr = new BufferedWriter(write_fr);

            String oneLine;
            int line_index = 1;
            while((oneLine = input_bfr.readLine())!= null)
            {
                result.addAll( this.recognize(oneLine, line_index) );
                line_index++;
            }

            for(Token t: result)
            {
                write_bfr.write("( "+t.getOrigin()+" , "+t.getClassification() + " )\r\n");//windows换行为\r\n
                write_bfr.flush();//压入文件
            }
            input_fr.close();
            input_bfr.close();
            write_fr.close();
            write_bfr.close();


        }
        catch (FileNotFoundException e)
        {
            System.out.println("读取文件失败: "+codeSourceFile);
            System.exit(0);
        }
        catch (IOException ioe)
        {
            System.out.println("读取文件失败: "+codeSourceFile);
            System.exit(0);
        }

        return result;
    }

    public ArrayList<Token> recognize(String line, int line_index)// line_index 是说 识别出问题时 的行号
    {
        StringBuilder line_deal = new StringBuilder(line);//句尾要加 \0帮助 完成此句最后一个token的识别
        line_deal.append('\0');
        char[] s = line_deal.toString().toCharArray();
        ArrayList<Token> result = new ArrayList<Token>();
        StringBuilder origin = new StringBuilder();//存储 识别token 的内容
        String current_status = "init";//记录当前的DFA状态
        String nowDFA = "null";
        for(int i=0; i<s.length; i++)
        {
            char one_char = s[i];
            //System.out.println("当前处理符号："+one_char);//###
            boolean deal_success = false;//对于该字符 是否有归处了 如果已经识别了这个字符那么就应该处理下一个 否则继续处理这一个字符
            String input;

            if( Character.isDigit(one_char) )
            {
                input = "digital";
            }
            else if( Character.isLetter(one_char))
            {
                input = "alpha";
                if( nowDFA.equals("const_DFA") )
                {
                    input = Character.toString(one_char);
                }
            }
            else
            {
                input = Character.toString(one_char);
            }
            //System.out.println("当前状态是："+current_status);//###
            //System.out.println("input is:"+input);//###

            //先判断是不是标识符（关键字）
            if( (this.forwardDFA(id_DFA, current_status, input) != null) && ( nowDFA.equals("null") || nowDFA.equals("id_DFA") ) )//在DFA中可以前进 说明情况正常
            {
                //System.out.println("在DFA中前进");//###
                nowDFA = "id_DFA";
                current_status = this.forwardDFA(id_DFA, current_status, input);//正常在DFA中前进
                //System.out.println("到达状态为："+current_status);//####
                origin.append( one_char );//正常前进要将 这个字符和前面的内容归到一起
                deal_success = true;//该字符处理成功
                continue;//跳出去处理下一个字符
            }
            else if( current_status.contains("terml") && nowDFA.equals("id_DFA") )//目标状态为空 说明前进不了  1.根本进入不了DFA 2.识别完成了(遇到下一个不是本类型的字符了） 3.迷失了
            {//识别完成
                if( this.isKeyWord( origin.toString() ) )//关键字是标识符的子集
                {
                    //System.out.println( "(" + origin.toString() + ",关键字）"  );
                    Token kyw_token = new Token();
                    kyw_token.setClassification("keyWord");
                    kyw_token.setOrigin( origin.toString() );
                    kyw_token.setToken_id(1);//关键字代号 1
                    kyw_token.setLine_Num(line_index);
                    result.add( kyw_token );
                }
                else//是标识符
                {
                    //System.out.println( "(" + origin.toString() + ",标识符）"  );
                    Token id_token = new Token();
                    id_token.setClassification("identifier");
                    id_token.setOrigin( origin.toString() );
                    id_token.setToken_id(2);// 标识符代号2
                    id_token.setLine_Num(line_index);
                    result.add( id_token );
                }
                nowDFA = "null"; //识别完成了 现在读进来的 字符就不属于这个DFA
                current_status = "init";//处理完成后 下一个字符 一个从头开始
                origin.delete( 0, origin.length() );//确定一个token了 就不需要保留它的内容
                deal_success = true;//这个字符处理成功 有归处了

            }
            else if( current_status.contains("init") )//根本进不了DFA
            {
                deal_success = false;//这个字符没有处理成功
                //这个 字符可能是 常量 or 界符 or 运算符 给下面处理
            }
            else// 在DFA中迷失了自己
            {
                if( nowDFA.equals("id_const") )
                {
                    origin.append(one_char);
                    System.out.println("非法标识符："+origin.toString()+"  出错行号"+line_index);
                    System.exit(0);
                }

            }


            //判断是不是 常量
            if( (this.forwardDFA(const_DFA, current_status, input) != null) && ( nowDFA.equals("null") || nowDFA.equals("const_DFA") ) )//在DFA中可以前进 说明情况正常
            {
                //System.out.println("在DFA中前进");//###
                nowDFA = "const_DFA";
                current_status = this.forwardDFA(const_DFA, current_status, input);//正常在DFA中前进
                //System.out.println("到达状态为："+current_status);//####
                origin.append( one_char );//正常前进要将 这个字符和前面的内容归到一起
                deal_success = true;//该字符处理成功
                continue;//跳出去处理下一个字符
            }
            else if( current_status.contains("terml") && nowDFA.equals("const_DFA"))//目标状态为空 说明前进不了  1.根本进入不了DFA 2.识别完成了(遇到下一个不是本类型的字符了） 3.迷失了
            {//识别完成

                //System.out.println( "(" + origin.toString() + ",常量）"  );
                Token const_token = new Token();
                const_token.setClassification("constant");
                const_token.setOrigin( origin.toString() );
                const_token.setToken_id(3);// 常量代号3
                const_token.setLine_Num(line_index);
                result.add( const_token );

                nowDFA = "null"; //识别完成了 现在读进来的 字符就不属于这个DFA
                current_status = "init";//处理完成后 下一个字符 一个从头开始
                origin.delete( 0, origin.length() );//确定一个token了 就不需要保留它的内容
                deal_success = true;//这个字符处理成功 有归处了

            }
            else if( current_status.contains("init") )//根本进不了DFA
            {
                deal_success = false;//这个字符没有处理成功
                //这个 字符可能是 常量 or 界符 or 运算符 给下面处理
            }
            else// 在DFA中迷失了自己
            {
                if( nowDFA.equals("const_DFA") )
                {
                    origin.append(one_char);
                    System.out.println("非法常量："+origin.toString()+"  出错行号"+line_index);
                    System.exit(0);
                }

            }
            if( input.equals("\n")||input.equals(" ")||input.equals("   " ) ||input.equals("\0") ||input.equals("\t"))//为什么不放前面 是因为这个 空格 帮助完成了识别
            {
                current_status = "init";
                nowDFA = "null";
                continue;//空格 什么的就不识别了
            }

            if( isDelimiter(input) )//看是不是 界符
            {
                //System.out.println("(" + input + ",界符）");
                Token delimiter = new Token();
                delimiter.setOrigin( input );
                delimiter.setClassification("delimiter");
                delimiter.setToken_id(4);
                delimiter.setLine_Num(line_index);
                result.add(delimiter);

                nowDFA = "null"; //识别完成了 现在读进来的 字符就不属于这个DFA
                current_status = "init";//处理完成后 下一个字符 一个从头开始
                origin.delete( 0, origin.length() );//确定一个token了 就不需要保留它的内容
                deal_success = true;//这个字符处理成功 有归处了
                continue;
            }
            if( isOper( input ) && isOper( Character.toString(s[i+1]) )  )
            {
                origin.append(input);
                origin.append(s[i+1]);

                if( isOper( origin.toString() ))
                {
                    //System.out.println("(" + origin.toString() + ",运算符）");
                    Token double_oper = new Token();
                    double_oper.setToken_id(5);
                    double_oper.setClassification("operate");
                    double_oper.setOrigin( origin.toString() );
                    double_oper.setLine_Num(line_index);
                    result.add(double_oper);
                }
                else
                {
                    //System.out.println("(" + input + ",运算符）");
                    //System.out.println("(" + s[i+1] + ",运算符）");
                    Token oper1 = new Token();
                    oper1.setOrigin(input);
                    oper1.setClassification("operate");
                    oper1.setToken_id(5);
                    oper1.setLine_Num(line_index);
                    result.add(oper1);
                    Token oper2 = new Token();
                    oper2.setOrigin( Character.toString(s[i+1]) );
                    oper2.setClassification("operate");
                    oper2.setToken_id(5);
                    oper2.setLine_Num(line_index);
                    result.add(oper2);

                }
                origin.delete( 0, origin.length() );//确定一个token了 就不需要保留它的内容
                i++;
                continue;
            }
            else if ( isOper(input) )
            {
                //System.out.println("(" + input + ",运算符）");
                Token oper = new Token();
                oper.setOrigin(input);
                oper.setClassification("operate");
                oper.setToken_id(5);
                oper.setLine_Num(line_index);
                result.add(oper);
                continue;
            }
            else
            {
                System.out.println("无法识别: "+input+ " 行号："+line_index);
                System.exit(0);
            }



        }
        return result;
    }

    public boolean isKeyWord( String  s )
    {
        for(String ky: this.keyWord)
        {
            if( ky.equals(s) )
                return true;
        }
        return false;
    }
    public  boolean isDelimiter( String s )
    {
        for( String de: delimiters)
        {
            if( de.equals(s) )
                return true;
        }
        return false;
    }
    public boolean isOper(String s)
    {
        for(String oper: operate)
        {
            if( oper.equals(s) )
                return true;
        }
        return false;
    }

    public String forwardDFA( ArrayList<Transform> DFA, String current_status, String input )
    {
        for(Transform trans: DFA)
        {
            if( trans.trans(current_status, input) != null )
                return trans.trans(current_status, input);
        }
        return null;
    }

    public void showTokenList(ArrayList<Token> tokenList)
    {
        for( Token t: tokenList)
        {
            if( t.getClassification().equals("keyWord") )
            {
                System.out.println( "(" + t.getOrigin() + ",关键字）"  );
            }
            if( t.getClassification().equals("delimiter") )
            {
                System.out.println("(" + t.getOrigin() + ",界符）");
            }
            if( t.getClassification().equals("identifier") )
            {
                System.out.println( "(" + t.getOrigin() + ",标识符）"  );
            }
            if( t.getClassification().equals("operate") )
            {
                System.out.println("(" + t.getOrigin() + ",运算符）");
            }
            if( t.getClassification().equals("constant") )
            {
                System.out.println( "(" + t.getOrigin() + ",常量）"  );
            }
        }
    }
}
